﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;


namespace WarningForeverClone.Input
{
    /// <summary>
    /// Mouse button enumeration
    /// </summary>
    public enum MouseButton { Left, Right, Middle };


    public class InputHandler
    {

        #region Fields

        //keyboard states
        /// <summary>
        /// New keyboard state
        /// </summary>
        private KeyboardState kNewState;
        /// <summary>
        /// Old keyboard state
        /// </summary>
        private KeyboardState kOldState;

        //mouse states
        /// <summary>
        /// New mouse state
        /// </summary>
        private MouseState mNewState;
        /// <summary>
        /// Old mouse state
        /// </summary>
        private MouseState mOldState;

        //xbox controller states
        /// <summary>
        /// New xbox controller state
        /// </summary>
        private GamePadState[] xNewState;
        /// <summary>
        /// Old xbox controller state
        /// </summary>
        private GamePadState[] xOldState;

        //Thumbstick menu navigation
        /// <summary>
        /// How long it has been since this thumbstick activated an "over" button press command
        /// </summary>
        float[] timeSinceMove;
        /// <summary>
        /// How long a thumbstick must be held in "over" position to activiate another "over" button press command
        /// </summary>
        const float thumbstickWaitTime = 0.2f;
        /// <summary>
        /// How far a thumbstick must be pushed to be considered over
        /// </summary>
        const float thumbstickOverAmount = 0.5f;

        /// <summary>
        /// Position of mouse
        /// </summary>
        private Point mousePosition;

        #endregion //fields

        #region Constructors

        /// <summary>
        /// Constructs the input manager
        /// </summary>
        /// <param name="numPlayersOnXbox">How many players on the xbox need input</param>
        public InputHandler()
        {
            mousePosition = new Point(0, 0);
            xNewState = new GamePadState[4];
            xOldState = new GamePadState[4];
            timeSinceMove = new float[4];
        }

        #endregion //constructors

        #region Methods

        /// <summary>
        /// Polls the mouse and get's its cursor's location
        /// </summary>
        private void mEnterUpdate()
        {
            mNewState = Mouse.GetState();
            mousePosition = new Point(mNewState.X, mNewState.Y);
        }
        /// <summary>
        /// Sets the old mouse state to the new mouse state
        /// </summary>
        private void mExitUpdate()
        {
            mOldState = mNewState;
        }
        /// <summary>
        /// Polls the keyboard
        /// </summary>
        private void kEnterUpdate()
        {
            kNewState = Keyboard.GetState();
        }
        /// <summary>
        /// Sets the old keyboard state to the new keyboard state
        /// </summary>
        private void kExitUpdate()
        {
            kOldState = kNewState;
        }
        /// <summary>
        /// Polls main player's controller
        /// </summary>
        private void xEnterUpdate(float deltaTime)
        {
            for (PlayerIndex index = PlayerIndex.One; index <= PlayerIndex.Four; index++)
            {
                xNewState[(int)index] = GamePad.GetState(index);
                timeSinceMove[(int)index] += deltaTime;
            }
        }
        /// <summary>
        /// Sets new state to old state
        /// </summary>
        private void xExitUpdate()
        {
            for (PlayerIndex index = PlayerIndex.One; index < PlayerIndex.Four; index++)
            {
                xOldState[(int)index] = xNewState[(int)index];
            }
        }

        /// <summary>
        /// Polls input devices
        /// </summary>
        public void UpdateInput(bool xboxInput, float deltaTime)
        {
            if (!xboxInput)
            {
                mExitUpdate();
                kExitUpdate();
                mEnterUpdate();
                kEnterUpdate();
            }
            else
            {
                xExitUpdate();
                xEnterUpdate(deltaTime);
            }
        }

        /// <summary>
        /// Gets the difference in scroll wheel values between current and previous state
        /// </summary>
        /// <returns>Scroll wheel value difference</returns>
        public int GetScrollDifference()
        {
            return mNewState.ScrollWheelValue - mOldState.ScrollWheelValue;
        }


        /// <summary>
        /// Sees if keyboard button is down
        /// </summary>
        /// <param name="key">Which key is being tested</param>
        /// <returns></returns>
        public bool IsKeyDown(Keys key)
        {
            if (kNewState.IsKeyDown(key))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// Sees if a keyboard button has been pressed and released
        /// </summary>
        /// <param name="key">Which key is being tested</param>
        /// <returns></returns>
        public bool KeyPressed(Keys key)
        {
            if (!kNewState.IsKeyDown(key) && kOldState.IsKeyDown(key))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Sees if a mouse button is down
        /// </summary>
        /// <param name="button">"Left", </param>
        /// <returns>true if down, false if not down</returns>
        public bool IsMouseButtonDown(MouseButton button)
        {
            switch (button)
            {
                case MouseButton.Left:
                    if (mNewState.LeftButton == ButtonState.Pressed)
                        return true;
                    break;

                case MouseButton.Right:
                    if (mNewState.RightButton == ButtonState.Pressed)
                        return true;
                    break;

                case MouseButton.Middle:
                    if (mNewState.MiddleButton == ButtonState.Pressed)
                        return true;
                    break;
            }

            return false;
        }
        /// <summary>
        /// Sees if a mouse button is was just released so that this state is released
        /// but the last one was pressed
        /// </summary>
        /// <param name="button">Which button</param>
        /// <returns>true if down, false if not down</returns>
        public bool IsMouseButtonJustReleased(MouseButton button)
        {
            switch (button)
            {
                case MouseButton.Left:
                    if (mNewState.LeftButton == ButtonState.Released && mOldState.LeftButton == ButtonState.Pressed)
                        return true;
                    break;

                case MouseButton.Right:
                    if (mNewState.RightButton == ButtonState.Released && mOldState.RightButton == ButtonState.Pressed)
                        return true;
                    break;

                case MouseButton.Middle:
                    if (mNewState.MiddleButton == ButtonState.Released && mOldState.MiddleButton == ButtonState.Pressed)
                        return true;
                    break;
            }

            return false;
        }
        /// <summary>
        /// Sees if a mouse button was just pressed so that this state is pressed
        /// but the last one was released
        /// </summary>
        /// <param name="button">Which button</param>
        /// <returns></returns>
        public bool IsMouseButtonJustPressed(MouseButton button)
        {
            switch (button)
            {
                case MouseButton.Left:
                    if (mNewState.LeftButton == ButtonState.Pressed && mOldState.LeftButton == ButtonState.Released)
                        return true;
                    break;

                case MouseButton.Right:
                    if (mNewState.RightButton == ButtonState.Pressed && mOldState.RightButton == ButtonState.Released)
                        return true;
                    break;

                case MouseButton.Middle:
                    if (mNewState.MiddleButton == ButtonState.Pressed && mOldState.MiddleButton == ButtonState.Released)
                        return true;
                    break;
            }

            return false;
        }

        /// <summary>
        /// Sees if xbox button is down
        /// </summary>
        /// <param name="button">Which button is being tested</param>
        /// <param name="playerIndex">Which player's button is being tested</param>
        /// <returns></returns>
        public bool IsXboxButtonDown(Buttons button, PlayerIndex index)
        {
            if (xNewState[(int)index].IsButtonDown(button))
                return true;
            else
                return false;
        }
        /// <summary>
        /// Was a certain button just pressed
        /// </summary>
        /// <param name="button">Which button</param>
        /// <param name="playerIndex">Which player</param>
        /// <returns>True if pressed, false if not pressed</returns>
        public bool IsXboxButtonPressed(Buttons button, PlayerIndex index)
        {
            if (xNewState[(int)index].IsButtonDown(button) && xOldState[(int)index].IsButtonUp(button))
                return true;
            else
                return false;
        }

        public bool IsXboxLeftThumbStickOver(int direction, PlayerIndex index)
        {
            if (direction == 0) //right
            {
                if (xNewState[(int)index].ThumbSticks.Left.X + thumbstickOverAmount > 1 &&
                    timeSinceMove[(int)index] > thumbstickWaitTime)
                {
                    timeSinceMove[(int)index] = 0.0f;
                    return true;
                }
            }
            else if (direction == 1) //up
            {
                if (xNewState[(int)index].ThumbSticks.Left.Y + thumbstickOverAmount > 1 &&
                    timeSinceMove[(int)index] > thumbstickWaitTime)
                {
                    timeSinceMove[(int)index] = 0.0f;
                    return true;
                }
            }
            else if (direction == 2) //left
            {
                if (xNewState[(int)index].ThumbSticks.Left.X - thumbstickOverAmount < -1 &&
                    timeSinceMove[(int)index] > thumbstickWaitTime)
                {
                    timeSinceMove[(int)index] = 0.0f;
                    return true;
                }
            }
            else if (direction == 3) //down
            {
                if (xNewState[(int)index].ThumbSticks.Left.Y - thumbstickOverAmount < -1 &&
                    timeSinceMove[(int)index] > thumbstickWaitTime)
                {
                    timeSinceMove[(int)index] = 0.0f;
                    return true;
                }
            }

            return false;
        }

        #endregion //methods

        #region Properties

        /// <summary>
        /// X position of mouse relative to top left of xna window
        /// </summary>
        public int MouseX
        {
            get
            {
                return mNewState.X;
            }
        }

        /// <summary>
        /// Y position of mouse relative to top left of xna window
        /// </summary>
        public int MouseY
        {
            get
            {
                return mNewState.Y;
            }
        }

        /// <summary>
        /// Old X position of mouse relative to top left of xna window
        /// </summary>
        public int OldMouseX
        {
            get
            {
                return mOldState.X;
            }
        }

        /// <summary>
        /// Old Y position of mouse relative to top left of xna window
        /// </summary>
        public int OldMouseY
        {
            get
            {
                return mOldState.Y;
            }
        }

        /// <summary>
        /// Mouse position in Point form
        /// </summary>
        public Point MousePosition
        {
            get
            {
                return new Point(mNewState.X, mNewState.Y);
            }
        }

        /// <summary>
        /// Mouse position in Vector2 form
        /// </summary>
        public Vector2 MouseVector
        {
            get
            {
                return new Vector2(mNewState.X, mNewState.Y);
            }
        }


        /// <summary>
        /// The position of the left thumbstick in the newest state
        /// </summary>
        public GamePadThumbSticks Thumbsticks(PlayerIndex index)
        {
            return xNewState[(int)index].ThumbSticks;
        }

        #endregion //properties

    }
}
